查看原文
其他

Google公司的软件工程之道 (1)

2017-02-14 朱少民等 软件质量报道


徐文锦 杨晓慧 朱少民 编译

英文原文由Google软件工程师Fergus Henderson 于2017年1月31日所写,文章末尾原文链接。


【本文将比较彻底、全面地分享谷歌公司的软件工程之道, 总共分为三部分,分四次发布,今天发布 Google软件开发之道中“测试、缺陷跟踪、编程语言、调试工具、发布工程、产品启动审批、事后分析、频繁重写”等内容】


4 测试Testing

 

单元测试是Google公司非常提倡和广泛采用的工程实践。产品线上所有的代码都要求进行单元测试,如果新添加的源文件没有进行相应的测试,代码审核工具将会将它们突显出来。代码评审者通常要求:任何增加了新功能的变更都应该添加新的测试来覆盖这项新功能。Mocking框架很受欢迎,在此框架下仍允许对于重量级库函数有依赖的代码进行轻量级单元测试的构建。

集成测试和回归测试也得到了广泛的应用。

正如上文“预提交检验”中提到的,测试自动被视作代码审核和提交流程中的一部分。

Google也有自动化工具来度量测试覆盖率,其结果作为可选层(anoptional layer),与源代码浏览器(窗口)集成在一起。

Google部署之前的压力测试也是必备礼仪(derigueur),要求团队通过图表显示在各种输入请求(负载压力)下系统的关键指标,如延迟和错误率。

[ 译者注:欲全面了解Google的软件测试,请参考James Whittaker等《Google软件测试之道》]

[译者注:补充一张照片,显示测试通过的绿灯] 

5 缺陷跟踪Bug tracking

谷歌使用一个命名为Buganizer的缺陷(Bug)跟踪系统来跟踪问题: Bug、特性请求、客户问题和流程(如产品发布或缺陷清理流程)Bug按层次化的组件/模块进行分类,每一组件会有一个默认的受让人(assignee,即将bug分配给这人)和邮件抄送列表。当发送变更的源代码供审核,系统会自动提示工程师,将变更与特定问题联系起来。

Google的团队中定期扫描各个组件中未关闭的Bug是十分常见的(虽然不很普遍),优先关注这些Bug并合理地将它们分配给相应的工程师。有些团队会有一个特定的人员来负责Bug分配(triage,译者注:有些bug处理有争议,需要特定的处理,就像微软早期的“”三国会议),而有些团队则在定期团队会议上进行做Bug分配。Google的很多团队均依据Bug上的标识(译者注:Bug状态标记)来判断此Bug是否已经被分配、每个Bug需要在哪个版本发布前被修复

 

2.6 编程语言

 

Google极力鼓励软件工程师去使用官方认可的四种编程语言C++、Java、Python或 Go中的一种进行编程。不同的编程语言使用数量达到最低,从而减少代码复用和程序员合作上的障碍。

Google为每种语言制定了编程规范,以确保整个公司的代码编写使用相似的风格、布局、命名约定等。除此之外,公司还有一套代码可读性培训流程。通常这项培训是让在意代码可读性且有经验的工程师来培训其他的工程师,针对特定的编程语言,通过审查实质性的变更或一系列变更,直到评审人员满意并认可作者知道如何编写易读的代码为止,从而培养他们如何写出可读性强、符合惯例的代码。在特定语言增加了任何有意义的、新代码的变更,均需要由一个通过该项语言可读性培训合格的人的批准,方可执行。

除了上述四种编程语言,也有许多专业的领域特定语言(Domain-Specific Languages,DSL是用于特殊用途的(如BUILD语言是用于指定构建目标及其依赖关系的)。

这些不同的编程语言之间的互操作主要是通过使用协议缓冲(ProtocolBuffers来实现的。协议缓冲是对结构化数据编码的一种有效的、可扩展的方法,它包括一种用于说明结构化数据的DSL,连同一个可以解析这样的描述并生成C++JavaPython代码的编译器, 完成这些对象的构造、访问、序列化和反序列化。Google版本的协议缓冲是与GoogleRPC库相集成的,允许简单的跨语言RPC实现,并对RPC框架自动处理的请求与响应进行序列化和反序列化。

过程的共性是在巨大的代码库和多样语言下依然能轻松进行软件开发的关键因素:无论什么项目或语言都使用一组相同的命令,来执行所有日常的软件工程任务(如代码检出、编辑、构建、测试、评审、提交代码、缺陷报告等等)。这样一来,不论开发人员正在编辑的代码来自不同的项目、还是用不同的语言编写的,他们都不需要重新学习一种新的开发流程。

 

2.7 调试和分析工具Debugging andProfiling tools

 

Google服务器与为正在运行的服务器进行调试的工具库是联系在一起的。一旦服务器崩溃,一个信号处理程序将堆栈跟踪信息自动存储到日志文件中,并保存其core文件。如果由于堆内存耗尽导致服务器崩溃的,服务器将保存一个堆对象的样本子集所在的那些站点的堆栈跟踪信息。也提供了Web的调试接口,允许检查传入和传出的RPC(包括时间、错误率、速率限制等)、改变命令行标志值(例如为特定模块增加日志冗长度)、资源消耗、分析等等。

这些工具大大增加整体调试的容易度,因此也很少启动像gdb那样的传统调试器。

 

2.8 发布工程Release engineering

 

Google,只有很少的几个团队有专职的的发布工程师,但大多数团队,其发布工程的工作是由非专职的软件工程师来完成的      

大多数软件发布频繁;每周或每两周发布一个版本是普遍的,甚至有些团队做到每日发布。可以这样做,是因为大部分常规的版本发布实现了自动化。频繁发布新版本有助于保持工程师的士气(如果几个月或几年才发布一个新版本,那么他们是很难保持兴奋的),并通过更快的迭代来提高了整体的开发效率,而且在给定的时间内有更多的机会获得用户的反馈和对反馈做出响应。

版本的发布通常开始于一个崭新的workspace通过同步最新的“绿色”版本(即最后一个通过所有自动测试的版本)的变更号并创建一个版本分支。发布工程师可以选择额外的变化作为“择优挑选(cherry-picked)”,即将主分支合并到新版本分支。然后软件会从头重新构建并执行响应的测试。如果测试失败,则会添加新的变更来修复失败,这“择优选取”的变更会再次合并到新版本分支,再重新构建和重新测试。直到测试全部通过后,将构建好的可执行文件和数据文件一起打包。所有这些步骤都是自动执行的,因此发布工程师只需要运行一些简单的命令,甚至只需要在菜单驱动的UI上选择相关项,选择哪些变更(如果有)作为“择优挑选”。

一旦候选版本已经完成打包,则通常部署到staging(译者注Beta 服务器或准产品线环境)服务器上,由少量用户(有时只是开发团队)进一步完成集成测试

一个有用的技术,涉及到从产品线上发送一份请求的copy(或子集)到staging服务器,同时也发送同样的请求到当前真正的产品服务器以进行实际的处理。从staging服务器获得的响应已被丢弃,而从真实的产品服务器所获得的响应会送回给用户。这有助于确保在新版本真正上线之任何有可能会导致严重后果的问题(如服务器崩溃)能被检测出来。

下一步通常是推出一个或多个canary服务器,用于处理产品线流量的一个子集(subset of thelive production traffic。与staging服务器不同,它是处理和应对真实用户的。

最后发布就可以推广到所有数据中心的服务器上了。一些非常高流量、高可靠性的服务是需要几天时间、逐步推出,以减少由于未被之前的检测步骤所发现的、新引入的缺陷而造成任何停机/服务中断(outages)的影响。

更多的Google发布工程信息,请参阅SRE一书的8 [7],或参考 [15]

[译者注:下面是我收集的一组Google的数据,估计可以让您震撼]


 

2.9 产品启动审批Launch approval

发布任何用户可见的变更或发布重大设计变更时,需要通过实施这项变更的核心工程团队以外的成员的审批。特别是审批(通常是受到详细审核)必须确保代码的编写符合法律要求、隐私需求、安全需求、可靠性需求(例如有合适的自动监测器来检测服务器停机,并自动通知相应的工程师)、业务需求等。

验收过程的设定也是为了确保在发布重大新产品或者新特性时能通知到公司内部相关的人。

Google有一个内部使用的验收审核工具,用于跟踪所需的评审及批准,确保符合每个产品定义的启动流程。这个工具很容易操作、并且可定制,因此不同的产品或产品领域可以按不同的要求进行审核和批准。

关于验收审批的更多信息,请参阅SRE一书的第27[7]

 

2.10 事后分析报告Post-mortems

 

每当生产系统有重大的outage或类似的事故发生时,所涉及到的人员都被要求写一份事后分析报告。该文档描述了这一事故,包括标题、摘要、影响、时间表、事故根源、做对/错了什么(whatworked/what didn’t)以及行动计划。分析重点在于问题本身以及未来该如何避免再次发生,而不是谁出的问题或该问题由谁负责。

  • 影响部分试图量化意外事件带来的影响,如停机多少时间、丢失了多少查询(或失败的RPC等)和损失了多少营业额等。

  • 时间表部分可以完整给出从停机开始到诊断、纠正问题这样一个过程。

  • 做对/错了什么:总结教训——哪些做法有助于快速检测并解决问题、哪些做错了、采取了哪些具体行动(最好是报成Bug再分配给特定的人)以此在将来减少发生此类问题的可能性和/或类似问题的严重性。

关于Google事后调查文化的更多信息,请参阅第SRE[7]15章。

 

2.11 频繁重写Frequent rewrites

 

Google大多数的软件每隔几年就会被重写一次。

这么做的成本之高,往往会是令人惊讶。事实上它也的确消耗了Google很大一部分的资源。但它仍然带来一些非常良好的收益,正是这些优点对Google敏捷性和长期成功起着关键的作用。近几年来,随着软件环境和其他技术的变化,产品的快速变化成为了一项典型的需求,随着技术或市场的变化也进一步影响用户的需求、渴望和期望。多年前发布的软件是围绕一组旧的设计要求来设计的,通常不适合当前的需求。此外,它通常会积累了大量的复杂性。重写代码削减掉所有不必要的复杂性,例如过去强调的需求在今天不再那么重要了。另外,重写代码也是将知识和归属感传递给新的团队成员的一种方式。这种归属感是生产力的关键——工程师们自然而然地会把更多的精力投入到开发并修复那些他们觉得是属于自己代码中的缺陷。频繁的重写也鼓励不同项目之间的工程师流动,以推进思想的交融。频繁的重写也有助于确保代码总是使用最新的技术和方法来写的。


您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存